#region Copyright (c) 2006-2011 Widgetsphere LLC, All Rights Reserved
//--------------------------------------------------------------------- *
//                          Widgetsphere  LLC                           *
//             Copyright (c) 2006-2011 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF WIDGETSPHERE LLC   *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS ANY WIDGETSPHERE PRODUCT.    *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF WIDGETSPHERE,      *
//INC.  THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO       *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Linq;
using Widgetsphere.Generator.Common.GeneratorFramework;
using Widgetsphere.Generator.Models;
using System.Text;
using Widgetsphere.Generator.Common.Util;
using System.Collections.Generic;
using Widgetsphere.Generator.Common;

namespace Widgetsphere.Generator.NHibernateFluent.Generators.Mappings
{
	public class MappingGeneratedTemplate : NHibernateBaseTemplate
	{
		private StringBuilder sb = new StringBuilder();
		private Table _currentTable = null;

		public MappingGeneratedTemplate(ModelRoot model, Table table)
		{
			_model = model;
			_currentTable = table;
		}

		#region BaseClassTemplate overrides
		public override string FileName
		{
			get { return string.Format("{0}Map.Generated.cs", _currentTable.PascalName); }
		}

		public string ParentItemName
		{
			get { return string.Format("{0}Map.cs", _currentTable.PascalName); }
		}

		public override string FileContent
		{
			get
			{
				GenerateContent();
				return sb.ToString();
			}
		}
		#endregion

		#region GenerateContent

		private void GenerateContent()
		{
			try
			{
				Widgetsphere.Generator.GenerationHelper.AppendCopyrightInCode(sb, _model);
				this.AppendUsingStatements();
				sb.AppendLine("namespace " + this.GetLocalNamespace());
				sb.AppendLine("{");
				this.AppendClass();
				sb.AppendLine("}");
				sb.AppendLine();
			}
			catch (Exception ex)
			{
				throw;
			}
		}

		private void AppendUsingStatements()
		{
			sb.AppendLine("using System;");
			sb.AppendLine("using System.Linq;");
			sb.AppendLine("using System.Runtime.Serialization;");
			sb.AppendLine();
		}

		private void AppendClass()
		{
			sb.AppendLine("	/// <summary>");
			sb.AppendLine("	/// ");
			sb.AppendLine("	/// </summary>");
			sb.AppendLine("	[DataContract]");
			sb.AppendLine("	[Serializable]");
			sb.AppendLine("	public partial class " + _currentTable.PascalName + "Map : FluentNHibernate.Mapping.ClassMap<" + this.DefaultNamespace + ".DataTransfer." + _currentTable.PascalName + ">");
			sb.AppendLine("	{");

			sb.AppendLine("		/// <summary>");
			sb.AppendLine("		/// ");
			sb.AppendLine("		/// </summary>");
			sb.AppendLine("		public " + _currentTable.PascalName + "Map()");
			sb.AppendLine("		{");
			sb.AppendLine();

			sb.AppendLine("			#region Rename Table");
			sb.AppendLine();
			if (_currentTable.DatabaseName != _currentTable.PascalName)
				sb.AppendLine("			this.Table(\"" + _currentTable.DatabaseName + "\");");
			
			if (_currentTable.Immutable)
				sb.AppendLine("			this.ReadOnly();"); ;

			sb.AppendLine();
			sb.AppendLine("			#endregion");
			sb.AppendLine();

			//Create annotations for properties
			sb.AppendLine("			#region Setup Fields");
			sb.AppendLine();
			sb.AppendLine("			//Field setup for " + _currentTable.PascalName + " entity");
			foreach (var column in _currentTable.GeneratedColumns)
			{
				sb.Append("			this.Map(x => x." + column.PascalName + ")");

				//Map to DB name
				if (column.PascalName != column.DatabaseName)
					sb.Append(".Column(\"" + column.DatabaseName + "\")");

				//Nullable
				if (column.AllowNull) sb.Append(".Nullable()");
				else sb.Append(".Not.Nullable()");

				//Default
				if (!string.IsNullOrEmpty(column.Default))
					sb.Append(".Default(\"" + column.Default + "\")");

				//Formula
				if (column.ComputedColumn && !string.IsNullOrEmpty(column.Formula))
					sb.Append(".Default(\"" + column.Formula + "\")");

				//Length
				if (column.IsTextType && column.Length != 0)
					sb.Append(".Length(" + column.GetAnnotationStringLength() + ")");

				//Unique
				if (column.IsUnique)
					sb.Append(".Unique()");

				if (column.Identity == IdentityTypeConstants.Database)
					sb.Append(".Generated.Insert()");

				sb.AppendLine(";");
			}
			sb.AppendLine();

			sb.AppendLine("			#endregion");
			sb.AppendLine();

			//Create annotations for relationships
			sb.AppendLine("			#region Relations");
			sb.AppendLine();

			foreach (Relation relation in _currentTable.GetRelations())
			{
				if (!relation.ParentTable.Generated || !relation.ChildTable.Generated)
				{
					//Do Nothing
				}
				
				else if (relation.ChildTable.Generated && !relation.ChildTable.IsInheritedFrom(_currentTable) && !relation.ChildTable.AssociativeTable)
				{
					sb.AppendLine("			//Relationship [" + _currentTable.DatabaseName + "] => [" + relation.ChildTable.DatabaseName + "]" + (string.IsNullOrEmpty(relation.PascalRoleName) ? "" : " (Relation: " + relation.PascalRoleName + ")"));
					if (relation.IsOneToOne)
					{
						sb.Append("			this.HasOne<" + this.DefaultNamespace + ".DataTransfer." + relation.ChildTable.PascalName + ">(x => x." + relation.PascalRoleName + relation.ChildTable.PascalName + ")");
						foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
						{
							Column parentColumn = columnRelationship.ParentColumnRef.Object as Column;
							Column childColumn = columnRelationship.ChildColumnRef.Object as Column;
							sb.Append(".ForeignKey(\"" + childColumn.DatabaseName + "\").PropertyRef(\"" + childColumn.PascalName + "\")");
						}
					}
					else
					{
						sb.Append("			this.HasMany<" + this.DefaultNamespace + ".DataTransfer." + relation.ChildTable.PascalName + ">(x => x." + relation.PascalRoleName + relation.ChildTable.PascalName + "List)");
						foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
						{
							Column parentColumn = columnRelationship.ParentColumnRef.Object as Column;
							Column childColumn = columnRelationship.ChildColumnRef.Object as Column;
							sb.Append(".KeyColumn(\"" + childColumn.DatabaseName + "\").PropertyRef(\"" + childColumn.PascalName + "\")");
						}
					}

					sb.AppendLine(";");
				}
				else if (relation.ChildTable.Generated && !relation.ChildTable.IsInheritedFrom(_currentTable) && relation.ChildTable.AssociativeTable)
				{
					//Associative relation
					IEnumerable<Relation> associativeRelations = relation.ChildTable.GetChildRoleRelationsFullHierarchy();
					Relation relation1 = associativeRelations.FirstOrDefault();
					Relation relation2 = associativeRelations.LastOrDefault();
					Table table1 = (Table)relation1.ParentTableRef.Object;
					Table table2 = (Table)relation2.ParentTableRef.Object;
					if (!table1.IsTypeTable && !table2.IsTypeTable && table1.Generated && table2.Generated)
					{
						var otherTable = table1;
						var theRelation = relation2;
						if (table1 == _currentTable)
						{
							otherTable = table2;
							theRelation = relation1;
						}
						sb.Append("			this.HasManyToMany<" + this.DefaultNamespace + ".DataTransfer." + otherTable.PascalName + ">(x => x." + otherTable.PascalName + "List)");
						sb.Append(".Table(\"" + relation.ChildTable.DatabaseName + "\")");
						foreach (ColumnRelationship columnRelationship in theRelation.ColumnRelationships)
						{
							Column parentColumn = columnRelationship.ParentColumnRef.Object as Column;
							Column childColumn = columnRelationship.ChildColumnRef.Object as Column;
							sb.Append(".ParentKeyColumn(\"" + parentColumn.PascalName + "\").PropertyRef(\"" + childColumn.PascalName + "\")");
						}
						sb.AppendLine(".Cascade.SaveUpdate();");
					}
				}

			}

			//Now loop though relations where I am a child entity
			foreach (Relation relation in _currentTable.GetRelationsWhereChild())
			{
				if (!relation.ParentTable.Generated || !relation.ChildTable.Generated)
				{
					//Do Nothing
				}
				else if (!relation.ParentTable.IsTypeTable && !relation.ChildTable.IsTypeTable && !relation.ChildTable.IsInheritedFrom(relation.ParentTable))
				{
					sb.Append("			this.References(x => x." + relation.PascalRoleName + relation.ParentTable.PascalName + ")");
					foreach (ColumnRelationship columnRelationship in relation.ColumnRelationships)
					{
						var parentColumn = columnRelationship.ParentColumnRef.Object as Column;
						var childColumn = columnRelationship.ChildColumnRef.Object as Column;
						sb.Append(".Column(\"" + parentColumn.PascalName + "\").ForeignKey(\"" + childColumn.PascalName + "\")");
						if (!parentColumn.AllowNull)
							sb.Append(".Not.Nullable()");
					}
					sb.AppendLine(";");
				}
			}

			sb.AppendLine();
			sb.AppendLine("			#endregion");
			sb.AppendLine();

			//Primary Keys
			sb.AppendLine("			#region Primary Key");
			sb.AppendLine();
			var columnList = _currentTable.GetColumns().Where(x => x.PrimaryKey).OrderBy(x => x.Name).ToList();
			if (columnList.Count == 1)
			{
				var c = columnList.First();
				sb.AppendLine("			this.Id(x => x." + c.PascalName + ", \"" + c.DatabaseName + "\");");
			}
			else
			{
				sb.Append("			this.CompositeId()");
				foreach (var c in columnList)
				{
					sb.Append(".KeyProperty(x => x." + c.PascalName + ", \"" + c.DatabaseName + "\")");
				}
				sb.AppendLine(";");
			}
			sb.AppendLine();

			sb.AppendLine("			#endregion");
			sb.AppendLine();

			sb.AppendLine("		}");
			sb.AppendLine();

			sb.AppendLine("	}");
			sb.AppendLine();

		}

	  #endregion

	}

}